#include "Subsystems/BlockBehaviors/SubsystemRotBlockBehavior.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Subsystems/SubsystemPickables.h"

#include "GameSingleton.h"
#include "GameRandom.hpp"
#include "GameRegistry.hpp"
#include "Managers/BlocksManager.h"
#include "Managers/GameManager.h"
#include "Log.h"

#include "Components/ComponentChest.h"
#include "Components/ComponentInventory.h"
#include "Components/ComponentFurnace.h"
#include "Game/IInventory.hpp"

// 食物腐烂行为
namespace PocketSurvival
{

    constexpr float m_rotPeriod = 60.0f;

    void SubsystemRotBlockBehavior::onPoll(int32_t value, int32_t x, int32_t y, int32_t z)
    {
        if(m_gameInfo->environmentBehaviorMode == EnvironmentBehaviorMode::Static)
        {
            return;
        }
        if(m_isRotEnabled == true)
        {
            int32_t num = Terrain::ExtractContents(value);
            Block *block = GameSingleton::blocksManager->blocks[num];
            int32_t rotPeriod = block->getRotPeriod(value);
            if (rotPeriod > 0 && GameSingleton::gameRandom->getInt(1, 65535) % rotPeriod == 0)
            {
            	int32_t num2 = block->getDamage(value) + 1;
            	value = (num2 > 1) ? block->getDamageDestructionValue(value) : block->setDamage(value, num2);
                m_terrain->changeCell(x, y, z, value);
            }
        }
    }

    void SubsystemRotBlockBehavior::handleInventory(IInventory *inventory, int32_t damageCount)
    {
        for(int32_t index = 0; index < inventory->slotsCount(); ++index)
        {
            int32_t slotCount = inventory->getSlotCount(index);
            if(slotCount > 0)
            {
                int32_t slotValue = inventory->getSlotValue(index);
                if(slotValue != 0)
                {
                    int32_t num2 = Terrain::ExtractContents(slotValue);
                    Block *block = GameSingleton::blocksManager->blocks[num2];
                    int32_t rotPeriod = block->getRotPeriod(slotValue);
                    if(rotPeriod > 0)
                    {
                        int32_t num3 = block->getDamage(slotValue);
                        for (int32_t i = 0; i < damageCount; i++)
                        {
                            if (num3 > 1)
                            {
                                break;
                            }
                            if ((i + m_rotStep) % rotPeriod == 0)
                            {
                                num3++;
                            }
                        }
                        if (num3 <= 1)
                        {
                            inventory->setSlotValue(index, block->setDamage(slotValue, num3), slotCount);
                        }
                        else
                        {
                            inventory->setSlotValue(index, block->getDamageDestructionValue(slotValue), slotCount);
                        }
                    }
                }
            }
        }
    }

    void SubsystemRotBlockBehavior::update(float dt)
    {
        int32_t damageCount = (int32_t)((m_gameInfo->totalElapsedGameTime - m_lastRotTime) / m_rotPeriod);
        if(damageCount > 0)
        {
            if(m_isRotEnabled == true)
            {
                auto chestView = GameSingleton::gameRegistry->view<ComponentChest*>();
                for(entt::entity entity : chestView)
                {
                    ComponentChest* chest = chestView.get<ComponentChest*>(entity);
                    handleInventory(chest, damageCount);
                }

                auto inventoryView = GameSingleton::gameRegistry->view<ComponentInventory*>();
                for(entt::entity entity : inventoryView)
                {
                    ComponentInventory* inventory = inventoryView.get<ComponentInventory*>(entity);
                    handleInventory(inventory, damageCount);
                }

                auto furnaceView = GameSingleton::gameRegistry->view<ComponentFurnace*>();
                for(entt::entity entity : furnaceView)
                {
                    ComponentFurnace* furnace = furnaceView.get<ComponentFurnace*>(entity);
                    handleInventory(furnace, damageCount);
                }

                std::vector<Pickable*> pickablesVec;
                m_pickables->getNowNotRemovePickables(pickablesVec);
                for(Pickable* pickable : pickablesVec)
                {
                    int32_t num2 = Terrain::ExtractContents(pickable->value);
                    Block *block = GameSingleton::blocksManager->blocks[num2];
                    int32_t rotPeriod = block->getRotPeriod(pickable->value);
                    if(rotPeriod > 0)
                    {
                        int32_t num3 = block->getDamage(pickable->value);
                        for (int32_t i = 0; i < damageCount; i++)
                        {
                            if (num3 > 1)
                            {
                                break;
                            }
                            if ((i + m_rotStep) % rotPeriod == 0)
                            {
                                num3++;
                            }
                        }
                        if (num3 <= 1)
                        {
                            pickable->value = block->setDamage(pickable->value, num3);
                        }
                        else
                        {
                            pickable->value = block->getDamageDestructionValue(pickable->value);
                        }
                    }
                }
            }
            m_rotStep += damageCount;
            m_lastRotTime += damageCount * m_rotPeriod;
        }
    }

    void SubsystemRotBlockBehavior::load(const nlohmann::json& json)
    {
        m_timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        m_terrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();
        m_gameInfo = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();
        m_pickables = GameSingleton::singletonPtr->getSingleton<SubsystemPickables>();

        Utils::GetJsonValue(json, m_lastRotTime, "LastRotTime", 0.0);
        Utils::GetJsonValue(json, m_rotStep, "RotStep", 0);

        m_isRotEnabled = (m_gameInfo->gameMode != GameMode::Creative && m_gameInfo->gameMode != GameMode::Adventure && m_gameInfo->areAdventureSurvivalMechanicsEnabled == true);
    }

    void SubsystemRotBlockBehavior::save(nlohmann::json& json)
    {
        json["LastRotTime"] = m_lastRotTime;
        json["RotStep"] = m_rotStep;
    }

    static const std::string subsystemName = "RotBlockBehavior";
    const std::string& SubsystemRotBlockBehavior::getName() const
    {
        return subsystemName;
    }

    SubLoadEchelon SubsystemRotBlockBehavior::getLoadEchelon()
    {
        return SubLoadEchelon::Second;
    }
    SubUpdateType SubsystemRotBlockBehavior::getUpdateType()
    {
        return SubUpdateType::MultiThreadUpdate;
    }

}
